$$.dd.DragDrop = $$.Class($$.Component, {
	constructor: function(el, config){
		this.el = $$.getEl(el);
		$$.put(this, config);
		
		this.addEvents(
			'beforestart'
			,'start'
			,'drag'
			,'beforedrop'
			,'drop'
			,'dragover'
			,'validdrop'
			,'invaliddrop'
		);
		
		this.__parent.constructor.call(this, config);
		this.init();
	}
	,init: function(){
		var me = this;
		this.canDrop = false;
		me.el.onMousedown(me._onStart.scope(me));
		me.el.onMouseup(me._onDrop.scope(me));
		
		if(this.dropTarget){
			if(!$$.isArray(this.dropTarget)) this.dropTarget = [this.dropTarget];
			for(var i = 0; i < this.dropTarget.length; ++i){
				this.dropTarget[i] = $$.getEl(this.dropTarget[i]);
			}
		};
		
		this.onInvaliddrop(function(){
			me.el.setStyle({cursor: 'not-allowed'});
		});
		
		this.onValiddrop(function(){
			me.el.setStyle({cursor: 'move'});
		});
		
		return this;
	}
	,_onStart: function(el, e){
		if($$.VALUE_HALT == this.fireEvent('beforestart', this, el, e)) return $$.VALUE_HALT;
		
		var me = this;
		
		if(this.dragProxy){
			var wrap = me.el.wrap({
				cls: me.el.dom.className
				,style: me.el.dom.getAttribute('style')
			});
			wrap.addClass('proxy').setStyle({position: 'relative'});
			wrap.setSize(me.el.getWidth(), me.el.getHeight(), 'b');
		};
		
		me.el.setStyle({position: 'absolute'});
		
		var elX = el.getX()
			,elY = el.getY();
		
		this.elXY = [elX, elY];
		this.offsetXY = [e.pageX - elX, e.pageY - elY];
		
		document.onmousemove = function(e){me._onDrag(el, e);};
		
		this.fireEvent('start', this, el, e);
		return this;
	}
	,_onDrag: function(el, e){
		el.setXY(e.pageX - this.offsetXY[0], e.pageY - this.offsetXY[1]);
		this.fireEvent('drag', this, el, e);
		
		if(!this.dropTarget) return;
		
		var target
			,eX
			,eY
			,targetX
			,targetY
			,targetWidth
			,targetHeight;
		
		for(var i = 0; i < this.dropTarget.length; ++i){
			target = this.dropTarget[i];
			if(	(eX = e.clientX) < (targetX = target.getX()) 
				|| eX > targetX + (targetWidth = target.getWidth()) 
				|| (eY = e.clientY) < (targetY = target.getY())
				|| eY > targetY + (targetHeight = target.getHeight())
			) continue;
			
			this.canDrop = target;
			this.fireEvent('validdrop', this, el, e, target);
			return this;
		}
		this.canDrop = false;
		this.fireEvent('invaliddrop', this, el, e);
		return this;
	}
	,_onDrop: function(el, e){
		if($$.VALUE_HALT == this.fireEvent('beforedrop', this, el, e)) return $$.VALUE_HALT;
		
		var me = this;
		document.onmousemove = null;
		
		if(this.dragProxy) el.unWrap();
		if(!this.dropTarget) return;
		
		el.setStyle({position: 'static'});
		el.setXY('auto');
		
		if(this.canDrop){
			this.canDrop.appendChild(me.el.dom);
			this.fireEvent('validdrop', this, el, e, this.canDrop);
		}
	}
});